`timescale 1ns / 1ps
// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

module master_ngsx #(parameter BYTE_REGS = 1,
					parameter RST_VALUE_IN = {BYTE_REGS*8{1'b0}},
					parameter RST_VALUE_OUT = {BYTE_REGS*8{1'b0}})                                     //to define the amount of byte registers that are Tx/Rx by this master module
   (
    //% active low reset signal (driven from internal FPGA logic)
    input                          iRst_n,
    //% serial clock (driven from internal PLL). SGPIO logic is clocked with this and used as SGPIO output as well
    input                          iClk,
    //% enable signal for this module, active high, if low, all outputs will be HIGH
    input                          iEna,
    //% Load signal (to SGPIO slave signals the 1st Tx bit and slave uses to latch parallel data as well)
    output reg                     oLoad_n,
    //% Parallel data from internal logic to SGPIO slave
    input [(BYTE_REGS*8)-1:0]      iPData,
    //% Serial data output to SGPIO slave
    output                         oSData,
    //% Serial data input (driven from SGPIO slave)
    input                          iSData,

    //% output clock signal (same as input, assigned here to make it in a modular way)
    output                         oClk,
    //% Parallel data to internal registers (master internal logic, clocked with SGPIO clk
    output reg [(BYTE_REGS*8)-1:0] oPData
    );

   ////////////////////////////////////////////////////////////////
   // local function to calculate the log-base2 of a number
   ///////////////////////////
   
   function integer clog2;
      input integer                   value;
      begin
         value = value-1;
         for (clog2=0; value>0; clog2=clog2+1)
           value = value>>1;
      end
   endfunction
 

   ///////////////////////////////////////////////////////////////
   // Local Param definitions
   ///////////////////////////
   localparam CNT_SIZE = clog2(BYTE_REGS*8);     //determines the bits required for the LDCNT 
   localparam [CNT_SIZE:0] MAX_LDCNT = (BYTE_REGS*8 - 1);     //determines the MAX cNT value for the LDCNT

   
   // Internal Signals
   
   
   reg [(BYTE_REGS*8)-1:0]         rSToPAcc;   //Serial to Parallel Accumulator (for serial data from SGPIO slave). Goes to internal registers
   reg [(BYTE_REGS*8)-1:0]         rPDataIn;   //parallel data input register (to latch data before serializing), goes to SGPIO slave

   reg [CNT_SIZE: 0]               ld_cnt;      //helps to generate the load pulse
         

   //////////////////////////////////////////////////////////////////////////////////
   // Continous assigments
   ////////////////////////
   
   assign oSData = iEna ? rPDataIn[(BYTE_REGS*8) - 1] : 1'b1;        //serial output is the MSb of the shifted register
      

   //////////////////////////////////////////////////////////////////////////////////
   // Sequential logic
   ///////////////////////
   
   
   always @(negedge iClk, negedge iRst_n)
     begin
        if (!iRst_n)   //Asynchronous reset condition for outputs and internal signals
          begin
             ld_cnt <= MAX_LDCNT;
             oLoad_n <= 1'b1;
             oPData <= RST_VALUE_IN;                   
             rPDataIn <= RST_VALUE_OUT;
             rSToPAcc <= RST_VALUE_IN;
          end
        else if (iEna)
          begin
             
             if (ld_cnt == 0)                       //load counter (decremental), wrap-around if = 0 
               begin
                  ld_cnt <= MAX_LDCNT;
                  oLoad_n <= 1'b1;                  //load output should be LOW at this moment, making it HIGH for the next clk cycle
               end
             else
               begin
                  ld_cnt <= ld_cnt - 1;             //we need to count
                  if (ld_cnt == 1)                  
                    begin                           //means next cycle, we are an MIN COUNT value and load has to be asserted then
                       oLoad_n <= 1'b0;               
                    end
                  else
                    begin                           //load has to be de-asserted (HIGH)
                       oLoad_n <= 1'b1;             
                    end
               end // else: !if(ld_cnt == 0)

             //logic for serial data coming from SGPIO Master that goes to internal logic
             rSToPAcc <= {rSToPAcc[(BYTE_REGS*8)-2:0], iSData};
             
             if (!oLoad_n)  //parallel data is captured to start serialization for data that goes to SGPIO Master
               begin
                  rPDataIn <= iPData;    //parallel data is captured in shift register to be serialized when Load==0
                  oPData <= rSToPAcc;    //parallel output is driven when Load==0 
               end //if (!oLoad_n)
             else 
               begin
                  rPDataIn[BYTE_REGS*8 - 1:0] <= {rPDataIn[BYTE_REGS*8 - 2:0], 1'b0};   //shifting register to serialize parallel input
               end //not(if (!oLoad_n))
          end // if (iEna)
        else
          begin
             ld_cnt <= MAX_LDCNT;
             oLoad_n <= 1'b1;
             oPData <= RST_VALUE_IN;                   
             rPDataIn <= RST_VALUE_OUT;
             rSToPAcc <= RST_VALUE_IN;
             
          end
        
     end // always @ (negedge iClk, negedge iRst_n)


   //output assignments

   assign oClk = iEna ? iClk : 1'b1;
   
endmodule // master_ngsx


